//
//  SketchUpdateController.m
//  RunSketch
//
//  Created by Brian Hinch on 7/27/08.
//  Copyright 2008 Tellart. All rights reserved.
//

#import "SketchUpdateController.h"

#define kCatalogURLTimeoutInterval 60.0
#define kDownloadURLTimeoutInterval 10.0
#define kTestURLTimeoutInterval 5.0
#define kDownloadTimerInterval      0.05


#define kCAT_EL_CATALOG             @"catalog"
#define kCAT_EL_ASSET               @"asset"
#define kCAT_ATR_TYPE               @"type"
#define kCAT_ATR_BASE               @"base"
#define kCAT_ATR_SRC                @"src"
#define kCAT_ATR_SIZE               @"size"
#define kCAT_ATR_FILES              @"files"
#define kASSET_TYPE_DIR             @"dir"
#define kASSET_TYPE_FILE            @"file"

#define kSketchIndexFileName            @"index"
#define kSketchIndexFileType            @"html"
#define kSketchReloadFileName           @"reload"
#define kSketchReloadFileType           @"html"
#define kLiveSketchDirectoryName        @"Sketch"
#define kRuntimeImageDirectoryName      @"__CAMERA"
#define kUpdatingSketchDirectoryName    @"Incoming"
#define kSketchingJSLibDirectoryName    @"SketchingLibrary"
#define kSketchingJSLibFileName         @"sketching"
#define kPrototypeJSLibFileName         @"prototype"
#define kJSLibFileType                  @"js"

@implementation SketchUpdateController

@synthesize delegate, doingTest, updating, downloading, rootURL, theConnection, receivedData,
sLiveSketchBasePath, sUpdatingSketchBasePath, baseURL, downloadRelativePathsQue;

-(id)init
{
    if (self = [super init])
    {
        NSLog(@"[SketchUpdateController init]");
    }
    return self;
}

    //determine the path the to the index.html file in the Resources directory
+ (NSString *) getSketchEntryPointPath {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *filePathString = [NSString stringWithFormat:@"%@/%@/%@.%@", [paths objectAtIndex:0], kLiveSketchDirectoryName, kSketchIndexFileName, kSketchIndexFileType];
    return filePathString;
}

+ (NSURL *) getSketchEntryPointURL {
    /*
    NSString *filePathString = [[NSBundle mainBundle]
        pathForResource:kSketchReloadFileName
        ofType:kSketchReloadFileType
        inDirectory:kSketchingJSLibDirectoryName
    ];
    */
    NSString *filePathString = [SketchUpdateController getSketchEntryPointPath];
    NSFileManager *fileman = [NSFileManager defaultManager];
    
    if ([fileman fileExistsAtPath:filePathString] == NO) {
        NSLog(@"SketchUpdateController: ALERT: User sketch does not exist, so sketch entry point URL will point to bundle (did not find %@)", filePathString );
        filePathString = [[NSBundle mainBundle]
            pathForResource:kSketchIndexFileName
            ofType:kSketchIndexFileType
            inDirectory:kLiveSketchDirectoryName
        ];
    }
    //return the URL and the request for the index.html file
    return [NSURL fileURLWithPath:filePathString];
}

+ (NSArray *) getSketchingJavaScriptLibraryURLs {
    //determine the path the to the index.html file in the Resources directory

    //prototype.js
    NSString *path1 = [[NSBundle mainBundle]
        pathForResource:kPrototypeJSLibFileName
        ofType:kJSLibFileType
        inDirectory:kSketchingJSLibDirectoryName
    ];
    NSURL *url1 = [NSURL fileURLWithPath:path1];
    NSLog(@"%@", url1);

    //sketching.js
    NSString *path2 = [[NSBundle mainBundle]
        pathForResource:kSketchingJSLibFileName
        ofType:kJSLibFileType
        inDirectory:kSketchingJSLibDirectoryName
    ];
    NSURL *url2 = [NSURL fileURLWithPath:path2];
    NSLog(@"%@", url2);
    
    return [NSArray arrayWithObjects: url1, url2, nil];
}

+ (NSString *)reserveRuntimeImagePath:(NSString *)base
{
    NSString *path;

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *filePathString = [NSString stringWithFormat:@"%@/%@/%@", [paths objectAtIndex:0], kLiveSketchDirectoryName, kRuntimeImageDirectoryName];
    NSFileManager *fileman = [NSFileManager defaultManager];
    
    //determine the base path
    
    BOOL directory;
    NSError *fsErr;
    
    //and create it again
    if ([fileman fileExistsAtPath:filePathString isDirectory:&directory] == NO) {
        //create the sketch directory
        if ([fileman createDirectoryAtPath:filePathString withIntermediateDirectories:YES attributes:nil error:&fsErr] == NO) {
            //failed to create photos path
            NSLog(@"SketchUpdateController: reserveRuntimeImagePath: ERROR creating %@ : %@", filePathString, [fsErr localizedDescription]);
            UIAlertView* alertView = [[UIAlertView alloc]
                initWithTitle:@"Could Not Save Image"
                message:[NSString stringWithFormat:@"Unable to save the camera image. Please ensure that your sketch does not contain a directory called \"%@\" and was not marked read-only before the sketch was uploaded.", kRuntimeImageDirectoryName]
                delegate:self
                cancelButtonTitle:@"Dismiss"
                otherButtonTitles:nil];
            [alertView show];
            [alertView release];
            return nil;
        }
    } else if (directory == NO) {
        //path already exists, but is not a directory
        UIAlertView* alertView = [[UIAlertView alloc]
            initWithTitle:@"Could Not Save Image"
            message:[NSString stringWithFormat:@"Unable to save the camera image. Please ensure that your sketch does not contain a file called \"%@\"", kRuntimeImageDirectoryName]
            delegate:self
            cancelButtonTitle:@"Dismiss"
            otherButtonTitles:nil];
        [alertView show];
        [alertView release];
        return nil;
    }
    
    //determine the file name
    NSTimeInterval ti = [[NSDate date] timeIntervalSince1970];
    path = [NSString stringWithFormat:@"%@/%@_%i.png", filePathString, base, ti];
    if([fileman fileExistsAtPath:path]) {
        if(NO == [fileman removeItemAtPath:path error:NULL]) {
            NSLog(@"Could not remove %@", path);
            return nil;
        }
    }
    
    [fileman release];
    
    return path;
}


/*
 * initiates a "live" update of the running sketch from an HTTP server
 * The server should respond with an XML file that describes all of the files/folders in the sketch.
 * RunSketch will then go through each entry in this file and start a new HTTP GET request
 * for each file. The results of these requests are saved to a temporary folder on
 * the mobile device. When each entry in the XML file has been downloaded this temporary
 * folder is swapped in for the real sketch folder and the WebView is reloaded.
 *
 */
- (void)updateFromURL:(NSURL *)url {
    if (doingTest || updating) return;
    
    //flag
    updating = YES;

    self.downloadRelativePathsQue = [NSMutableArray array];
    
    NSURLRequest *theRequest=[NSURLRequest requestWithURL:url
        cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
        timeoutInterval:kCatalogURLTimeoutInterval];
    
    //store for later use
    self.rootURL = url; //[url autorelease];
    
    NSLog(@"[SketchUpdateController updateFromURL:%@]", self.rootURL);
    
    self.theConnection = [[[NSURLConnection alloc] initWithRequest:theRequest delegate:self] autorelease];
    if (!self.theConnection) {
        // deal with this event
        //note that this is intentionally NOT in a performSelectorOnMainThread
        NSLog(@"[SketchUpdateController updateFromURL] could not create a connection to the update server at %@", self.rootURL);
        [self event_didFailWithConnectionError];
        //[self event_didFailWithConnectionError];
    }
}

- (void)testFromURL:(NSURL *)url {
    if (doingTest || updating) return;
    
    //flag
    doingTest = YES;
    
    NSURLRequest *theRequest=[NSURLRequest requestWithURL:url
        cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
        timeoutInterval:kTestURLTimeoutInterval];
    
    //store for later use
    self.rootURL = url; //[url autorelease];
    
    NSLog(@"[SketchUpdateController testFromURL:%@]", self.rootURL);

    self.theConnection = [[[NSURLConnection alloc] initWithRequest:theRequest delegate:self] autorelease];
    if (!self.theConnection) {
        // deal with this event
        //note that this is intentionally NOT in a performSelectorOnMainThread
        NSLog(@"[SketchUpdateController updateFromURL] could not create a connection to the test server at %@", self.rootURL);
        [self event_didFailWithConnectionError];
        //[self event_didFailWithConnectionError];
    }
}

/*
 * stops any update already in progress
 *
 */
- (void)stop {
    NSLog(@"SketchUpdate: stop called...");
    
    if (self.theConnection) {
        [self.theConnection cancel];
        NSLog(@"SketchUpdate: NSURLConnection cancelled.");
    }
    //prepare the filesystem for future updates
    if (!doingTest) {
        [self prepareFilesystem];
    }

    updating = NO;
    doingTest = NO;
    downloading = NO;

    [self performSelectorOnMainThread:@selector(event_didStop) withObject:nil waitUntilDone:YES];
    //[self event_didStop];
}

/*** urlconnection methods ******************************************************/

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    // this method is called when the server has determined that it
    // has enough information to create the NSURLResponse
 
    // it can be called multiple times, for example in the case of a
    // redirect, so each time we reset the data.
    // receivedData is declared as an ivar elsewhere
    if (updating) {
        self.receivedData = [NSMutableData data];
        [self performSelectorOnMainThread:@selector(event_didChooseSketch) withObject:nil waitUntilDone:YES];
    }
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    if (doingTest) return;

    // append the new data to the receivedData
    // receivedData is declared as a method instance elsewhere
    [receivedData appendData:data];

    sketchBytesLoaded += [data length];

    // deal with this event
    //[self performSelectorOnMainThread:@selector(event_didFailWithConnectionError) withObject:nil waitUntilDone:YES];
    [self event_madeProgress:currentDownloadIndex:totalNumDownloads];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    // release the connection, and the data object
    self.theConnection = nil;
    
    // inform the user
    if (doingTest) {
        //note that this is intentionally NOT in a performSelectorOnMainThread
        [self event_hostDidFailTest];
    } else {
        NSLog(@"[SketchUpdateController connection didFailWithError]: %@", error);
        [self performSelectorOnMainThread:@selector(event_didFailWithConnectionError) 
            withObject:[NSString stringWithFormat:@"ERROR: SketchUpdatecontroller connection failed! Error - %@ %@",
              [error localizedDescription],
              [[error userInfo] objectForKey:NSErrorFailingURLStringKey]]
            waitUntilDone:YES];
        /*[self event_didFailWithCommunicationError:
            [NSString stringWithFormat:@"ERROR: SketchUpdatecontroller connection failed! Error - %@ %@",
              [error localizedDescription],
              [[error userInfo] objectForKey:NSErrorFailingURLStringKey]]];
        */
    }
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    self.theConnection = nil;
    if (doingTest) {
        //note that this is intentionally NOT in a performSelectorOnMainThread
        [self event_hostDidPassTest];
        
    } else {

        if (downloading == YES) {
            [self didFinishLoadingFile];
        } else if (updating) {
            [self didFinishLoadingCatalog];
        }
        self.receivedData = nil;
    }
}

- (void)didFinishLoadingFile {
    //NSLog(@"SketchUpdateController: didFinishLoadingFile: Donwloaded file %i", currentDownloadIndex);
 
    // do something with the data
    //NSLog(@"didFinishLoadingFile");

    NSString *sRelativePath = [self.downloadRelativePathsQue objectAtIndex:currentDownloadIndex];
    NSString *sAbsolutePath = [self.sUpdatingSketchBasePath stringByAppendingString:sRelativePath];
    NSError *fsErr;
    
    //increment the download index
    currentDownloadIndex ++;
    
    //NSLog(@"SketchUpdateController: didFinishLoadingFile: saving to %@", sAbsolutePath);
    //save the data to disk
    BOOL success = [receivedData writeToFile: sAbsolutePath options: 0 error: &fsErr];

    //reset the data
    [receivedData setLength:0];

    //set flag
    downloading = NO;
    
    if (success == NO) {
        //error saving file
        //[self event_didFailWithCommunicationError: [NSString stringWithFormat:@"SketchUpdateController: didFinishLoadingFile: ERROR saving downloaded file %@ : %@", sAbsolutePath, [fsErr localizedDescription]]];
		NSLog(@"SketchUpdateController: didFinishLoadingFile: ERROR saving downloaded file, skipping %@ : %@", sAbsolutePath, [fsErr localizedDescription]);
        return;
    }
}
- (void)didFinishLoadingCatalog {
    // receivedData is declared as a method instance elsewhere
    //NSLog(@"SketchUpdateController: didFinishLoadingFile: Succeeded! Received %d bytes of data",[receivedData length]);

    // notify the delegate
    [self performSelectorOnMainThread:@selector(event_didLoadCatalog) withObject:nil waitUntilDone:YES];
    //[self event_didLoadCatalog];

    // prepare the filesystem
    if ([self prepareFilesystem] == NO) {
        // could not prepare the filesystem, so do not continue with the update
        // notify the delegate
        NSLog(@"[SketchUpdateController didFinishLoadingCatalog] could not prepare the file system");
        [self performSelectorOnMainThread:@selector(event_didFailWithConnectionError) withObject:nil waitUntilDone:YES];
        //[self event_didFailWithConnectionError];
        return;
    }
    
    //zero the byte counter
    sketchBytesLoaded = 0;
    sketchBytesTotal = 0;
    
    // construct an NSXMLParser to examine the catalog
    NSXMLParser *parser = [[NSXMLParser alloc] initWithData:receivedData];

    // Set self as the delegate of the parser so that it will receive the parser delegate methods callbacks.
    [parser setDelegate:self];
    // Depending on the XML document you're parsing, you may want to enable these features of NSXMLParser.
    [parser setShouldProcessNamespaces:NO];
    [parser setShouldReportNamespacePrefixes:NO];
    [parser setShouldResolveExternalEntities:NO];
    
    totalNumDownloads = 0;

    // start parsing the catalog
    [parser parse];
    
    NSError *parseError = [parser parserError];
    [parser release];
    
    if (parseError) {
        // notify the delegate of an error parsing the catalog
        NSLog(@"[SketchUpdateController didFinishLoadingCatalog] catalog parse error: %@", parseError);
        [self performSelectorOnMainThread:@selector(event_didFailWithCatalogParserError:)
            withObject:parseError
            waitUntilDone:YES];
        //[self event_didFailWithCatalogParserError:parseError];
        return;
    }

    //start the downloads
    [self startDownloads];
}

- (void) startDownloads {
    NSLog(@"****** startDownloads *******");
    
    //prepare for downloading files
    currentDownloadIndex = 0;
    downloading = NO;
    
    //schedule a timer to call downloadFromTimer
    [NSTimer scheduledTimerWithTimeInterval: kDownloadTimerInterval
        target: self
        selector: @selector(downloadFromTimer:)
        userInfo: nil
        repeats: YES];
}

- (BOOL)prepareFilesystem {
    //we'll need a file manager for this
    NSFileManager *fileman = [NSFileManager defaultManager];
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSError *fsErr;

    //get the "live" sketch base path
    self.sLiveSketchBasePath = [NSString stringWithFormat:@"%@/%@", [paths objectAtIndex:0], kLiveSketchDirectoryName];
    
    //make sure it exists
    if ([fileman fileExistsAtPath:self.sLiveSketchBasePath] == NO) {
        //create the sketch directory
        if ([fileman createDirectoryAtPath:self.sLiveSketchBasePath withIntermediateDirectories:YES attributes:nil error:&fsErr] == NO) {
            //error creating directory
            NSLog(@"SketchUpdateController: prepareFilesystem: ERROR creating live sketch dir: %@", [fsErr localizedDescription]);
            return NO;
        }
    }
    
    //get the "updating" sketch base path
    self.sUpdatingSketchBasePath = [NSString stringWithFormat:@"%@/%@", [paths objectAtIndex:0], kUpdatingSketchDirectoryName];
    
    //if it exists, remove it
    if ([fileman fileExistsAtPath:self.sUpdatingSketchBasePath] == YES) {
        //NSLog(@"SketchUpdateController: prepareFilesystem: will remove %@", self.sUpdatingSketchBasePath);
        if ([fileman removeItemAtPath:self.sUpdatingSketchBasePath error:&fsErr] == NO) {
            //error removing directory
            NSLog(@"SketchUpdateController: prepareFilesystem: ERROR removing updating sketch dir: %@", [fsErr localizedDescription]);
            return NO;
        }
    }
    
    //and create it again
    if ([fileman fileExistsAtPath:self.sUpdatingSketchBasePath] == NO) {
        //create the sketch directory
        if ([fileman createDirectoryAtPath:self.sUpdatingSketchBasePath withIntermediateDirectories:YES attributes:nil error:&fsErr] == NO) {
            //error creating directory
            NSLog(@"SketchUpdateController: prepareFilesystem: ERROR creating updating sketch dir: %@", [fsErr localizedDescription]);
            return NO;
        }
    }
    
    //cleanup
    /*
    [fileman release];
    [paths release];
    [fsErr release];
    */
    
    return YES;
}

/*** xml parsing methods ******************************************************************/

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
    NSScanner *strToInt;
    NSString *sRelativePath;
    NSString *sAbsolutePath;
    NSString *sFileSize;
    NSString *sSketchBytesTotal;
    int iFileSize = 0;
    
    /*
     * <kCAT_EL_ASSET> element
     */
    if ([elementName isEqualToString:kCAT_EL_ASSET]) {
        //the asset's relative path is stored in its kCAT_ATR_SRC attribute
        sRelativePath = [NSString stringWithFormat:@"%@", [attributeDict valueForKey:kCAT_ATR_SRC]];
        sFileSize = [NSString stringWithFormat:@"%@", [attributeDict valueForKey:kCAT_ATR_SIZE]];
        if (sFileSize) {
            strToInt = [NSScanner scannerWithString:sFileSize];
            if ([strToInt scanInt:&iFileSize] == NO) iFileSize = 0;
        } else {
            iFileSize = 0;
        }
        
        /*
         * kASSET_TYPE_FILE elements
         */ 
        if ([[attributeDict valueForKey:kCAT_ATR_TYPE] isEqualToString:kASSET_TYPE_FILE]) {
            //NSLog(@"SketchUpdateController: catalog file (%i bytes) - %@", iFileSize, sRelativePath);
            [self.downloadRelativePathsQue addObject:sRelativePath];
            
            //this seems to be a bit faster than [downloadRelativePathsQue count]
            totalNumDownloads++;

        /*
         * kASSET_TYPE_DIR elements
         */ 
        } else if ([[attributeDict valueForKey:kCAT_ATR_TYPE] isEqualToString:kASSET_TYPE_DIR]) {
            //NSLog(@"SketchUpdateController: catalog dir - %@", sRelativePath);
            //create the directory in the updating sketch base dir
            sAbsolutePath = [self.sUpdatingSketchBasePath stringByAppendingString:sRelativePath];
            if ([[NSFileManager defaultManager] createDirectoryAtPath:sAbsolutePath attributes:nil] == NO) {
                NSLog(@"SkethcUpdateController: ERROR creating local path %@", sAbsolutePath);
            }
        }
    
    /*
     * <kCAT_EL_CATALOG> element
     */
    } else if ([elementName isEqualToString:kCAT_EL_CATALOG]) {
        //record the kCAT_ATR_BASE attribute in an instance var
        self.baseURL = [NSString stringWithFormat:@"%@", [attributeDict valueForKey:kCAT_ATR_BASE]];
        sSketchBytesTotal = [NSString stringWithFormat:@"%@", [attributeDict valueForKey:kCAT_ATR_SIZE]];
        if (sFileSize) {
            strToInt = [NSScanner scannerWithString:sSketchBytesTotal];
            if ([strToInt scanInt:&sketchBytesTotal] == NO) sketchBytesTotal = 0;
        } else {
            sketchBytesTotal = 0;
        }
        
    }
    
    //record the last element for the next time this method is called
    lastElementName = elementName;
    
    //cleanup
    /*
    [strToInt release];
    [sRelativePath release];
    [sAbsolutePath release];
    [sRelativeURL release];
    [sFileSize release];
    [sSketchBytesTotal release];
    */
}

/* start the next download */
- (void)downloadFromTimer:(NSTimer *)theTimer {
    //NSLog(@"*TIMER* SketchUpdateController: downloadFromTimer updating=%i downloading=%i currentDownloadIndex=%i totalNumDownloads=%i", updating, downloading, currentDownloadIndex, totalNumDownloads);

    //don't start the next download if we're no longer updating
    if (updating == NO) {
        //no more future downloads either, stop the timer
        [theTimer invalidate];
        // all done
        return;
    }

    //don't start the next download if one is already in progress
    if (downloading == YES) return;
    
    //check if there are actually any more downloads to do
    if (currentDownloadIndex >= totalNumDownloads) {
        //All downloads are complete, stop the timer
        [theTimer invalidate];
        //move the updated sketch into the live position
        [self replaceLiveSketchWithUpdatingSketch];
        // notify the delegate
        [self performSelectorOnMainThread:@selector(event_didSucceed) withObject:nil waitUntilDone:YES];
        //[self event_didSucceed];
        // all done
        return;
    }

    //get the URL and paths of the next thing to download
    NSString *sRelativePath = [self.downloadRelativePathsQue objectAtIndex:currentDownloadIndex];
    NSString *sRelativeURL = [[NSString stringWithFormat:@"%@%@", self.baseURL, sRelativePath] stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
    
    //create a request for the file's URL (relative to rootURL)
    //NSURL *reqUrl = [NSURL URLWithString:sRelativeURL relativeToURL:self.rootURL];

    NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:sRelativeURL relativeToURL:self.rootURL]
        cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
        timeoutInterval:kDownloadURLTimeoutInterval];

    //NSLog(@"SketchUpdateController: downloadFromTimer: Starting download %i (there are %i downloads total): %@ (%@)", currentDownloadIndex, totalNumDownloads, theRequest, sRelativeURL);
        
    //create a urlconnection from the request
    self.theConnection = [[[NSURLConnection alloc] initWithRequest:theRequest delegate:self] autorelease];
    if (self.theConnection) {
        downloading = YES;
    } else {
        //could not start a download!
        NSLog(@"SkethcUpdateController: ERROR could not start download of %@", sRelativeURL);

        //Failure, stop the timer
        [theTimer invalidate];
        
        // deal with this event
        [self performSelectorOnMainThread:@selector(event_didFailWithConnectionError) withObject:nil waitUntilDone:YES];
        //[self event_didFailWithConnectionError];
        
        // all done
        return;
    }
}

-(BOOL)replaceLiveSketchWithUpdatingSketch {
    //we'll need a file manager for this
    NSFileManager *fileman = [NSFileManager defaultManager];
    NSError *fsErr;

    //if it exists, remove the live sketch
    if ([fileman fileExistsAtPath:self.sLiveSketchBasePath] == YES) {
        NSLog(@"SketchUpdateController: replaceLiveSketchWithUpdatingSketch: will remove %@", self.sLiveSketchBasePath);
        if ([fileman removeItemAtPath:self.sLiveSketchBasePath error:&fsErr] == NO) {
            //error removing directory
            NSLog(@"SketchUpdateController: replaceLiveSketchWithUpdatingSketch: ERROR removing live sketch dir: %@", [fsErr localizedDescription]);
            return NO;
        }
    } else {
        NSLog(@"SketchUpdateController: replaceLiveSketchWithUpdatingSketch: ERROR live sketch dir does not exist!! %@", self.sLiveSketchBasePath);
    }
    
    //if it exists, move the updating sketch to the live sketch
    if ([fileman fileExistsAtPath:self.sUpdatingSketchBasePath] == YES) {
        NSLog(@"SketchUpdateController: replaceLiveSketchWithUpdatingSketch: will move %@ to %@", self.sUpdatingSketchBasePath, self.sLiveSketchBasePath);
        if ([fileman moveItemAtPath: self.sUpdatingSketchBasePath
            toPath:self.sLiveSketchBasePath
            error:&fsErr] == NO) {
            //error removing directory
            NSLog(@"SketchUpdateController: replaceLiveSketchWithUpdatingSketch: ERROR moving updating sketch dir to live sketch dir: %@", [fsErr localizedDescription]);
            return NO;
        }
    } else {
        NSLog(@"SketchUpdateController: replaceLiveSketchWithUpdatingSketch: ERROR updating sketch dir does not exist!! %@", self.sUpdatingSketchBasePath);
    }
    
    return YES;
    
}


/*** event processing methods below here *********************************************************/
-(void) event_hostDidPassTest
{
    if (!doingTest) return;
    doingTest = NO;
    if ([self.delegate conformsToProtocol:@protocol(SketchUpdateControllerDelegate)]) {
        [self.delegate didFinishTest:self result:YES];
    }
}

-(void) event_hostDidFailTest
{
    if (!doingTest) return;
    doingTest = NO;
    if ([self.delegate conformsToProtocol:@protocol(SketchUpdateControllerDelegate)]) {
        [self.delegate didFinishTest:self result:NO];
    }
}

-(void) event_didChooseSketch
{
    if (!updating) return;
    if ([self.delegate conformsToProtocol:@protocol(SketchUpdateControllerDelegate)]) {
        [self.delegate didChooseSketch:self];
    }
}

-(void) event_didFailWithConnectionError
{
    //set flags
    if (!updating) return;
    updating = NO;
    downloading = NO;
    if ([self.delegate conformsToProtocol:@protocol(SketchUpdateControllerDelegate)]) {
        [self.delegate didFailWithConnectionError:self];
    }
}
-(void) event_didFailWithCommunicationError:(NSString *)msg {
    //set flags
    if (!updating) return;
    updating = NO;
    downloading = NO;
    [self.delegate didFailWithCommunicationError:self message:msg];
}
- (void) event_didFailWithCatalogParserError:(NSError *)error {
    //set flags
    if (!updating) return;
    updating = NO;
    downloading = NO;
    if ([self.delegate conformsToProtocol:@protocol(SketchUpdateControllerDelegate)]) {
        if (error && (error.domain == NSXMLParserErrorDomain)) {
            [self.delegate didFailWithCatalogParserError:self error:error];
        } else {
            [self.delegate didFailWithConnectionError:self];
        }
    }
}
- (void) event_didLoadCatalog {
    if (!updating) return;
    [self.delegate didLoadCatalog:self];
}
-(void) event_madeProgress: (int)bytesLoaded: (int)bytesTotal {
    if (!updating) return;
    [self.delegate madeProgress:self loaded:bytesLoaded total:bytesTotal];
}
-(void) event_didStop {
    //set flags
    updating = NO;
    downloading = NO;
    [self.delegate didStop:self];
}
-(void) event_didSucceed {
    //set flags
    updating = NO;
    downloading = NO;
    [self.delegate didSucceed:self];
}

/***********************************************************************/

- (void)dealloc {
    NSLog(@"[SketchUpdateController dealloc]");
    [rootURL release];
    [theConnection release];
    [receivedData release];
    [downloadRelativePathsQue release];
    [sLiveSketchBasePath release];
    [sUpdatingSketchBasePath release];
    [baseURL release];
    [receivedData release];
	[super dealloc];
}

@end
